/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       game_window.h

	$Header: /game/game_window.h $

	$NoKeywords: $

 ************************************************************************/
#ifndef GAME_WINDOW_H_INCLUDED
#define GAME_WINDOW_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#pragma warning( disable: 4786 )

#include <list>
#include "clip_list.h"
#include "counted_ptr.h"
#include "handler.h"
#include "mouse_window_ptr.h"
#include "screen_point.h"
#include "window_clip_list.h"
#include "uncopyable.h"

class  t_drag_object;
struct t_mouse_event;
class  t_paint_surface;
class  t_window;

// -----------------------------------------------------------------------
// basic window class
// -----------------------------------------------------------------------
class t_window : public t_counted_object, public t_uncopyable
{
public:
	enum t_transparency
	{
		k_no_transparency,
		k_some_transparency,
		k_completely_transparent
	};

	t_window( t_screen_rect const& rect, t_transparency transparency, t_window* parent );
	virtual ~t_window();

	static void close_help_balloon(); // close any current help balloon.
	static void close_system();       // called when the entire system is to shut down.

	bool					accept_drag( t_mouse_event const& event);
	static void				begin_drag( t_drag_object* drag_object ); // called to begin a drag operation
	static void				cancel_drag();
	bool					capture_mouse();
	void					close( bool close_child_windows = true );
	virtual void            drag_event( t_mouse_event const& event ); // called when a drag might occur
	void				    fit_to_parent_rect();
	virtual t_window*		get_child( t_screen_point point, bool ignore_visibility = false );  // find window from client coordinates
	int						get_child_count() const;
	t_window_list::iterator get_children_begin();
	t_window_list::iterator get_children_end();
	t_window_list::const_reverse_iterator get_children_rbegin() const;
	t_window_list::const_reverse_iterator get_children_rend() const;
	t_screen_rect			get_client_rect() const;
	t_mouse_window*			get_cursor( bool return_drag = true ) const;
	static t_drag_object*   get_drag_object();
	int                  get_height() const;
	std::string          get_help_balloon_text() const;
	virtual std::string  get_help_text( t_screen_point point ) const;
	static t_window*     get_keyboard_focus(); // get target of keystrokes
    static t_window*     get_main_window(); // get the topmost window
	static t_window*     get_modal_window(); // get the window to receive messages
	static t_window*     get_mouse_window( t_screen_point point );
	t_window*            get_parent() const;
	std::string          get_right_click_text() const;
	t_screen_rect const& get_screen_rect() const;
	t_transparency       get_transparency() const;
	int                  get_width() const;
	static t_window*     get_window( t_screen_point point ); // find window from screen coordinates
	bool                 has_captured_mouse() const;
	void                 invalidate();
	void                 invalidate( t_screen_rect const& rect );
	void				 invalidate( t_clip_list const& regions );
	static void          invalidate_main_window( t_screen_rect const& rect );
	                     // called only when a windows WM_PAINT message occurs.
	                     // returns true if point (in client coordinates) is over the object.
	virtual bool         is_contained( t_screen_point point ) const; 
	bool                 is_child( t_window const* child ) const; // is this a parent of child
	bool                 is_open() const;
	bool                 is_visible() const;	
  virtual void		 left_button_down( t_mouse_event const& event );
  virtual void		 left_button_up( t_mouse_event const& event );
	virtual void         left_double_click( t_mouse_event const& event );
	virtual bool         menu_click( int id );
  virtual void		 mouse_leaving( t_window* old_window, t_window* new_window,
		                                t_mouse_event const& event );
    virtual void		 mouse_move( t_mouse_event const& event );
	void                 move( t_screen_point point );
	void                 move( t_screen_rect const& rect, bool move_children = true );
	                     // in client coordinates of parent
	void                 move_before( t_window* window );
	void                 move_before( t_window_list::iterator index );
	void                 move_screen_rect( t_screen_rect const& rect, bool move_children = true );
	void                 move_to_back();
	void                 move_to_front();

	virtual void         on_close();
	virtual void		 on_keyboard_focus_lost();
	virtual void         on_size_change( t_screen_rect const& rect );
	void				 open( t_screen_rect const& rect, t_window* parent );
	static void          open_help_balloon( t_screen_point point );
	int                  run_modal( bool use_current_handlers = false );
	virtual void         paint( t_paint_surface& paint_surface );

	void                 paint_transparency( t_paint_surface& paint_surface,
	                                         t_window_list& list ); // paint an obscured area
	static void			 paint_windows();
	static bool          painting_needed();
	void				 release_keyboard_focus();
	void                 release_mouse();
    virtual void		 right_button_down( t_mouse_event const& event );
    virtual void		 right_button_up( t_mouse_event const& event );
	virtual void         right_double_click( t_mouse_event const& event );
	void                 set_cursor( t_mouse_window* cursor, bool include_children = true );
	void                 set_drop_shadow( bool arg = true );
	void                 set_help_balloon_text( std::string const& text );
	void				 set_keyboard_focus();
	void                 set_right_click_text( std::string const& text );
	void                 set_size_from_children();
	void                 set_visible( bool arg );
                    
	t_screen_point		 to_client( t_screen_point const& point ) const;
	t_screen_rect		 to_client( t_screen_rect  const& rect ) const;
	t_screen_point		 to_parent( t_screen_point const& point ) const;
	t_screen_rect		 to_parent( t_screen_rect  const& rect ) const;
	t_screen_point		 to_screen( t_screen_point const& point ) const;
	t_screen_rect        to_screen( t_screen_rect  const& rect ) const;
	virtual void         update_size();  // update size based on child windows
	virtual void         update_transparency();
protected:
	t_window( t_transparency transparency, t_window* parent ); // constructor for derived classes.

	virtual bool	accept_drag( t_drag_object* drag_object, t_mouse_event const& event );
	virtual void	add_child( t_window* window ); // add a child window
	void			init( t_screen_rect const& rect ); // designed to be called in a constructor.
	virtual void	on_visibility_change();

	t_transparency m_has_transparency;  // true if window has transparent areas.
	int            m_return_value;
private:
	void			clear_obscured_regions();
	void			close_children();
	void			close_window();
	t_screen_rect	get_clipped_rect() const;      // get screen rect clipped by parents
	void			get_obscured_child_regions( t_window_clip_list& regions ) const; // get obscured areas in
	                                                                  // children
	// compute visible / obscured regions without making changes
	void          get_visible_regions( t_clip_list& visible_regions, 
                                       t_window_clip_list& obscured_regions,                 
                                       t_screen_rect const& parent_rect ) const;

	void          invalidate_screen_region( t_clip_list const& regions, // invalidate in screen
		                                    bool pass_upwards );		// coordinates
	                                                                      
	 // paint the changes for this window and its children
	void          paint_changes();
	void          remove_child( t_window* child );                      // remove a child window
	void		  remove_from_drawing();
	void          remove_regions( t_clip_list& regions, t_screen_rect const& parent_rect ) const;
	                                                                    // clip regions from a list
	void          set_child_cursor( t_mouse_window* cursor ); 
	void          set_visible_regions( t_clip_list& visible_regions,    // determine clipping lists
		                               t_window_clip_list& obscured_regions,                 
									   t_screen_rect const& parent_rect );


	t_clip_list             m_changed_regions;          // in client coordinates
	t_window_list           m_children;
	t_mouse_window_ptr      m_cursor;                  // cursor to use in this window
	t_window_ptr            m_drop_shadow;              // drop shadow window.
	std::string             m_help_text;
	bool                    m_in_drawing_queue;        // already in list to be drawn.
	t_window_list::iterator m_list_position;           // where this window is in parent's list.
	bool                    m_mouse_captured;
	bool                    m_open;                    // window has been added to its parent
	t_window_clip_list      m_obscured_regions;         // in screen coordinates, less child regions
	t_window*               m_parent;					// parent window.  Not a window
														// ptr to avoid circular references
	std::string             m_right_click_text;
	t_screen_rect           m_screen_rect;              // area covered by window
	bool                    m_visible;                  // true if window should be drawn
	t_clip_list             m_visible_regions;          // visible rects, less child regions
};


// -----------------------------------------------------------------------
// inlines for basic window class
// -----------------------------------------------------------------------
inline int t_window::get_child_count() const
{
	return m_children.size();
}

inline t_window_list::iterator t_window::get_children_begin()
{
	return m_children.begin();
}

inline t_window_list::iterator t_window::get_children_end()
{
	return m_children.end();
}

inline t_window_list::const_reverse_iterator t_window::get_children_rbegin() const
{
	return m_children.rbegin();
}

inline t_window_list::const_reverse_iterator t_window::get_children_rend() const
{
	return m_children.rend();
}

inline t_window* t_window::get_parent() const
{
	return m_parent;
}

inline t_screen_rect const& t_window::get_screen_rect() const
{
	return m_screen_rect;
}

inline t_screen_rect t_window::get_client_rect() const
{
	return t_screen_rect( 0, 0, m_screen_rect.width(), m_screen_rect.height() );
}

inline std::string t_window::get_help_balloon_text() const
{
	return m_help_text;
}

inline int t_window::get_height() const
{
	return m_screen_rect.height();
}

inline std::string t_window::get_right_click_text() const
{
	return m_right_click_text;
}

inline t_window::t_transparency t_window::get_transparency() const
{
	return m_has_transparency;
}

inline int t_window::get_width() const
{
	return m_screen_rect.width();
}

inline bool t_window::has_captured_mouse() const
{
	return m_mouse_captured;
}

inline bool t_window::is_open() const
{
	return m_open;
}

inline bool t_window::is_visible() const
{
	return m_visible;
}

inline void t_window::set_help_balloon_text( std::string const& text )
{
	m_help_text = text;
}

inline void t_window::set_right_click_text( std::string const& text )
{
	m_right_click_text = text;
}

inline t_screen_point t_window::to_client( t_screen_point const& point ) const
{
	return point - m_screen_rect.top_left();
}

inline t_screen_rect t_window::to_client( t_screen_rect const& rect ) const
{
	return rect - m_screen_rect.top_left();
}

inline t_screen_point t_window::to_parent( t_screen_point const& point ) const
{
	if (m_parent != 0)
		return m_parent->to_client( point );
	return point;
}

inline t_screen_rect t_window::to_parent( t_screen_rect const& rect ) const
{
	if (m_parent != 0)
		return m_parent->to_client( rect );
	return rect;
}

inline t_screen_point t_window::to_screen( t_screen_point const& point ) const
{
	return point + m_screen_rect.top_left();
}

inline t_screen_rect t_window::to_screen( t_screen_rect const& rect ) const
{
	return rect + m_screen_rect.top_left();
}

inline t_window* t_window::get_window( t_screen_point point )
{
	t_window* frame_window = get_main_window();

	return frame_window->get_child( frame_window->to_client( point ) );
}

#include "mouse_window.h"

#endif